home *** CD-ROM | disk | FTP | other *** search
/ Mac Power 1997 October / MACPOWER-1997-10.ISO.7z / MACPOWER-1997-10.ISO / AMUG / PROGRAMMING / Mac F2C 1.3.5.sit / Mac F2C 1.3.5 / Mac F2C Libraries / libI77 Sources / DoMultiTask.c < prev    next >
C/C++ Source or Header  |  1997-01-20  |  12KB  |  477 lines

  1.  
  2. /****************************************************************************
  3. *                                                                            *
  4. *    This code implements a simple but flexible function    that can be            *
  5. *    called to add multitasking to Fortran code ported to the Macintosh        *
  6. *    using Mac F2C and the MetroWerks, THINK, or    Symantec C/C++ compilers.    *
  7. *                                                                            *
  8. *    This code is based extensively on code provided by:                        *
  9. *                                                                            *
  10. *        Mel    Martinez                                                        *
  11. *        The    Johns Hopkins University                                        *
  12. *        Dept. of Physics                                                    *
  13. *        mem@jhu.edu                                                            *
  14. *                                                                            *
  15. *    However, any and all bugs present are my responsiblity.                    * 
  16. *        IMT    16Sep95                                                            *
  17. *                                                                            *
  18. ****************************************************************************/
  19.  
  20.  
  21.  
  22.  
  23.  
  24.  
  25. /***************************************************************************
  26.  
  27.     Metrowerks CodeWarrior version
  28.     ******************************
  29.  
  30.     The approach is simple.  At program startup, F2Cmain() calls InitMultiTask()
  31.     which set up a counter that keeps track of the ticks elapsed.   It also 
  32.     initializes the spinning cursors (if the required resource is present; 
  33.     otherwise it fails without impacting anything). 
  34.  
  35.     Then whenever you call the domultitask_ (or DoMultiTask) function, it checks
  36.     if enough ticks have elapsed.  If so it calls WNE and spins the cursor.  If
  37.     WNE returns an event, it is passed to the SIOUX console to handle.
  38.  
  39.     At program completion, F2Cmain() calls EndMultiTask() which cleans up.
  40.  
  41. ***************************************************************************/
  42.     
  43. #ifdef CW_F2C
  44.  
  45. #include <Events.h>
  46. #include <OSUtils.h>
  47. #include <Quickdraw.h>
  48. #include <ToolUtils.h>
  49. #include <Resources.h>
  50.  
  51. #include <SIOUX.h>   
  52.  
  53.  
  54. /* Declare f2c library function used to recover from exception exits & aborts */
  55. void sig_die( char*, int );
  56.  
  57. /* Cursor related functions */
  58. Boolean InitAnimatedCursors( short acurID );
  59. void ReleaseAnimatedCursors( void );
  60. void SpinMyCursor( void );
  61.  
  62. /* Private globals */
  63. static long            gNextCheck;            /* Used to hold tick counts */
  64. static long            gTickSlice = 2;        /* 1/30th second in ticks (each tick = 1/60th sec) */
  65.  
  66.  
  67. /**********************************************************************
  68. *                                                                      *
  69. *    The    slice parameter above is the control that sets exactly           *
  70. *    how often the program will actually    call WaitNextEvent.              *
  71. *                                                                      *
  72. *        - The above    value is the recommended rate for assuring          *
  73. *          that user    interaction    is not affected    (even QuickTime          *
  74. *          movies should    still run fine in the foreground).              *
  75. *                                                                      *
  76. *        - If you want to be    less friendly and use more CPU              *
  77. *          time,    increase this number.                                  *
  78. *                                                                      *
  79. **********************************************************************/
  80.  
  81.  
  82.  
  83.  
  84. /*
  85.     Use this in F2Cmain.c to initialize the multi-tasking code
  86. */
  87.  
  88. void InitMultiTask( long sliceInTicks ) 
  89. {
  90.     if ( sliceInTicks > 0 )
  91.         gTickSlice = sliceInTicks;    
  92.  
  93.     InitAnimatedCursors( 128 );
  94.     
  95.     /* Start the tick count */
  96.     gNextCheck = TickCount() + gTickSlice;
  97. }
  98.  
  99.  
  100.  
  101.  
  102.  
  103. /*
  104.     Use this in F2Cmain.c to close-out the multi-tasking code
  105. */
  106.  
  107. void EndMultiTask( void )
  108. {
  109.     ReleaseAnimatedCursors();
  110. }
  111.  
  112.  
  113.  
  114.  
  115.  
  116. /*
  117.     Get to this call in your FORTRAN code by inserting CALL DOMULTITASK( sleepTime )
  118. */
  119.  
  120. int domultitask_( long *sleepTime )
  121. {
  122.     extern Boolean SIOUXQuitting;
  123.     EventRecord myEvent;
  124.  
  125.      if( TickCount() > gNextCheck )                       /* Time to check for events again? */
  126.     {
  127.          if( WaitNextEvent( everyEvent, &myEvent, *sleepTime, NULL ) )
  128.           {
  129.               /* Restore arrow while we're handling a real event */
  130.               SetCursor( &(qd.arrow) );
  131.               
  132.               /* Need to do something with the event if we got one */
  133.             /* Add additional event handling code here if you need it */
  134.             SIOUXHandleOneEvent( &myEvent );      
  135.             if( SIOUXQuitting )
  136.                 sig_die("User interrupt; execution stopped", 1);        /* Graceful quit */
  137.          }
  138.         SpinMyCursor();                                 /* Spin the cursor */
  139.         gNextCheck = TickCount() + gTickSlice;            /* Reset the tick count */
  140.     }
  141.     
  142.     return 1;
  143. }
  144.  
  145.  
  146.  
  147. /* 
  148.     Add this function so users can add DoMultiTask(n) calls to the
  149.     C output from Mac F2C if they prefer, instead of inserting
  150.     CALL DOMULTITASK(n) in the FORTRAN code 
  151. */
  152.  
  153. void DoMultiTask( long sleepTime )
  154. {
  155.     domultitask_( &sleepTime );
  156. }
  157.  
  158.  
  159.  
  160.  
  161.  
  162.  
  163. /********************
  164.  
  165.     Following are functions related to making the cursors spin.
  166.  
  167.     Wrote my own instead of using the InitCursors()/SpinCursor()/RotateCursor()
  168.     package because it is an incomplete port from the MPW environment found in
  169.     the MPW ToolLibs libraries and linking against them in the incorrect order
  170.     can give rise to subtle inconsistencies.  A likely source of problems for users.
  171.  
  172. ********************/
  173.  
  174.  
  175.  
  176. typedef struct        /* My version of the structure of an 'acur' resource */
  177. {
  178.     short numberOfFrames;            /* Number of cursors to animate */
  179.     short whichFrame;                /* Current frame number    */
  180.     CursHandle frame[];                /* Pointer to the first cursor */
  181. } acur, *acurPtr, **acurHandle;
  182.  
  183. static acurHandle gCursorList;        /* The cursor list */
  184.  
  185.  
  186.  
  187. /* 
  188.     Try to get the acur record and the cursor list for acurID, 
  189.     returning 1 (= TRUE) if everything goes as planned. 
  190. */
  191.  
  192. Boolean InitAnimatedCursors( short acurID )
  193. {
  194.     register short i = 0;
  195.     register short cursID;
  196.     Boolean noErrFlag = 0;
  197.  
  198.     if ( gCursorList = (acurHandle) GetResource( 'acur',acurID ) ) 
  199.     {
  200.         /* Got it! */ 
  201.         noErrFlag = 1;
  202.         while ( (i < (*gCursorList)->numberOfFrames) && noErrFlag ) 
  203.         {
  204.             /* The id of the cursor is stored in the high word of the frame handle */ 
  205.             cursID = (short) HiWord( (long) (*gCursorList)->frame[i] );
  206.             (*gCursorList)->frame[i] = GetCursor( cursID );
  207.             if ( (*gCursorList)->frame[i] )
  208.                 i++;                                /* Get the next one */
  209.             else
  210.                 noErrFlag = 0;                        /* Couldn't find the cursor */
  211.         }
  212.     }
  213.     
  214.     if ( noErrFlag ) 
  215.     {
  216.         (*gCursorList)->whichFrame = 0;
  217.     }
  218.     else
  219.     {
  220.         /* Free up memory we won't use */
  221.         if ( gCursorList )
  222.         {
  223.             (*gCursorList)->numberOfFrames = i;        /* These are all we managed to get */
  224.             ReleaseAnimatedCursors();
  225.         }
  226.         gCursorList = NULL;
  227.     }
  228.     
  229.     return noErrFlag;
  230. }
  231.  
  232.  
  233.  
  234.  
  235. /* 
  236.     Free up the storage used by the current animated cursor 
  237.     and all of its frames 
  238. */
  239.  
  240. void ReleaseAnimatedCursors( void )
  241. {
  242.     short i;
  243.     if ( gCursorList )
  244.     {
  245.         for ( i = 0; i< (*gCursorList)->numberOfFrames; i++ )
  246.             ReleaseResource( (Handle) (*gCursorList)->frame[i] );
  247.         ReleaseResource( (Handle) gCursorList );
  248.     }
  249. }
  250.  
  251.  
  252.  
  253. /*     
  254.     Display the next frame in the sequence. 
  255. */
  256.  
  257. void SpinMyCursor( void )
  258. {
  259.     if ( gCursorList )
  260.     {
  261.         /* Grab the frame, increment (and reset, if necessary) the count, */
  262.         /* and display the new cursor */
  263.         SetCursor( *((*gCursorList)->frame[(*gCursorList)->whichFrame++]) );
  264.         if( (*gCursorList)->whichFrame == (*gCursorList)->numberOfFrames )
  265.             (*gCursorList)->whichFrame = 0;
  266.     }
  267. }
  268.  
  269.  
  270. #endif    /* CW_F2C */
  271.  
  272.  
  273.  
  274.  
  275.  
  276.  
  277. /***************************************************************************
  278.  
  279.     THINK (68K) and Symantec (PPC) version
  280.     **************************************
  281.  
  282.     The THINK console does not have to pass an event back to it (i.e., an 
  283.     equivalent of CW's SIOUXHandleOneEvent function).  So we can't call
  284.     WNE directly because if we get an event there' no way to hand it back 
  285.     to the console (sorry, setting the event mask in WNE to 0x0000 or 0x0001
  286.     to get only null events doesn't work--nice idea though).  
  287.  
  288.     So instead have to do the following:
  289.     (a) Set stdin to raw, unbuffered mode.
  290.     (b) Call getchar() which forces the console to call WNE for us.  
  291.         Because we are in raw mode, getchar() returns immediately even if
  292.         the user doesn't type anything.
  293.     (c) Set stdin back to buffered mode.
  294.  
  295.     Alas, changing the mode of stdin is very slow.  So instead of implementing
  296.     the above directly, we make raw/unbuffer the 'default' mode on stdin so 
  297.     we don't have to change modes just to call WNE (via getchar).  And we
  298.     compensate by changing back to buffered mode prior to doing any input on 
  299.     stdio (and then restoring raw/unbuffered mode).  (InitMultiTask() and
  300.     EndMultiTask() are used to adjust the default mode on stdio).
  301.  
  302.     Luckily for this plan, the f2c library bottlenecks all input operations
  303.     through fread(), getc(), and ungetc().  Our replacements for these are
  304.     defined below.  They simply pass the call through to the 'real' versions 
  305.     of the above functions, changing modes appropriately if the file used 
  306.     for I/O is stdin.
  307.  
  308.     My versions of these functions are patched into the f2c library code
  309.     via macros in TPM_I.h and TPM_F.h.
  310.  
  311.     Many thanks to Phil Shapiro of Symantec for suggesting this approach.
  312.  
  313.     Also, there is no cursor spinning in this TPM/SPM version because the 
  314.     console updates the cursor on every opportunity, so all we do is end 
  315.     up fighting the console for control of the cursor's appearance.
  316.  
  317. ***************************************************************************/
  318.     
  319.  
  320. #if defined(TPM_F2C) || defined(SPM_F2C)
  321.  
  322. /* Undefine some #defines that come from TPM_I.h and SPM_I.h that we don't want here */
  323. #undef fread
  324. #undef __getc
  325. #undef ungetc
  326. #include <stdio.h>            /* We want the original unsubstituted versions */
  327.  
  328. #include <Events.h>
  329. #include <OSEvents.h>
  330. #include <OSUtils.h>
  331.  
  332. #include <console.h>
  333.  
  334. /* Private globals */
  335. static long            gNextCheck = 0;        /* Used to hold tick counts */
  336. static long            gTickSlice = 2;        /* 1/30th second in ticks (each tick = 1/60th sec) */
  337.  
  338. /**********************************************************************
  339. *                                                                      *
  340. *    The    slice parameter above is the control that sets exactly           *
  341. *    how often the program will actually    call WaitNextEvent.              *
  342. *                                                                      *
  343. *        - The above    value is the recommended rate for assuring          *
  344. *          that user    interaction    is not affected    (even QuickTime          *
  345. *          movies should    still run fine in the foreground).              *
  346. *                                                                      *
  347. *        - If you want to be    less friendly and use more CPU              *
  348. *          time,    increase this number.                                  *
  349. *                                                                      *
  350. **********************************************************************/
  351.  
  352.  
  353.  
  354.  
  355. /*
  356.     This is used in F2Cmain.c to initialize the multi-tasking code
  357. */
  358.  
  359. void InitMultiTask( long sliceInTicks ) 
  360. {
  361.     if ( sliceInTicks > 0 )
  362.         gTickSlice = sliceInTicks;    
  363.     
  364.     /* Set console so we can interogate for input (to trigger WNE) without waiting for input */
  365.     csetmode( C_RAW, stdin );
  366.  
  367.     /* Start the tick count */
  368.     gNextCheck = TickCount() + gTickSlice;
  369. }
  370.  
  371.  
  372.  
  373.  
  374.  
  375. /*
  376.     This is used in F2Cmain.c to close-out the multi-tasking code
  377. */
  378.  
  379. void EndMultiTask( void )
  380. {
  381.     csetmode(C_ECHO, stdin);     /* Restore standard, buffered mode (not really required :) */
  382. }
  383.  
  384.  
  385.  
  386.  
  387.  
  388. /*
  389.     Get to this call in your FORTRAN code by inserting CALL DOMULTITASK( sleepTime )
  390. */
  391.  
  392. int domultitask_( long *sleepTime )
  393. {
  394.      if( TickCount() > gNextCheck )                   /* Time to check for events again? */
  395.     {
  396.         getchar();                                   /* poll for char to have console call WNE */
  397.         gNextCheck = TickCount() + gTickSlice;        /* Reset the tick count */
  398.     }
  399.     return 1;
  400. }
  401.  
  402.  
  403.  
  404. /* 
  405.     Add this function so users can add DoMultiTask(n) calls to the
  406.     C output from Mac F2C if they prefer, instead of inserting
  407.     CALL DOMULTITASK(n) in the FORTRAN code 
  408. */
  409.  
  410. void DoMultiTask( long sleepTime )
  411. {
  412.     domultitask_( &sleepTime );
  413. }
  414.  
  415.  
  416.  
  417. /*
  418.     Because now stdin is (by default) in raw mode, must re-set it before reading
  419.     from it.  These replacement functions cover all the input functions used 
  420.     in the f2c libs.  #defines in TPM_F2C and SPM_F2C ensure that these are called
  421.     instead of their originals
  422. */
  423.  
  424.  
  425. size_t F2C_fread( void *ptr, size_t size, size_t n, FILE *f )
  426. {
  427.     size_t  nr;
  428.     if ( f == stdin )
  429.     {
  430.         csetmode(C_ECHO, stdin); 
  431.         nr = fread( ptr, size, n, f );
  432.         csetmode(C_RAW, stdin);
  433.     }
  434.     else
  435.         nr = fread( ptr, size, n, f );
  436.  
  437.     return  nr;
  438. }
  439.  
  440.  
  441. int F2C_getc( FILE * f)
  442. {
  443.     int i;
  444.     if ( f == stdin )
  445.     {
  446.         csetmode(C_ECHO, stdin); 
  447.         i = __getc( f );
  448.         csetmode(C_RAW, stdin);
  449.     }
  450.     else
  451.         i = __getc( f );
  452.  
  453.     return  i;
  454. }
  455.  
  456.  
  457. int F2C_ungetc( int c, FILE *f )
  458. {
  459.     int i;
  460.     if ( f == stdin )
  461.     {
  462.         csetmode(C_ECHO, stdin); 
  463.         i = ungetc( c, f );
  464.         csetmode(C_RAW, stdin);
  465.     }
  466.     else
  467.         i = ungetc( c, f );
  468.  
  469.     return  i;
  470. }
  471.     
  472.  
  473. #endif    /* TPM_F2C or SPM_F2C */
  474.  
  475.  
  476.  
  477.